// ****************************************************************************
//	Copyright (c) 2011 Daniel Watman
//	http://www.keycommander.net/
// ****************************************************************************
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//	
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//	
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
// ****************************************************************************
//	File: USB.c
//	Purpose: functions for USB connectivity using MAX3420 USB perhiperal controller
//	Author:	Daniel Watman
//	Created: 2011.02.28
//	Updated: 2011.02.28
//	Version: 0.1
//	Target:	Atmel AVR XMEGA 128A1
//	Compiler: AVR-GCC
// ****************************************************************************
//	Changes:
//		0.1	* Initial version
// ****************************************************************************
//	To do:
//		* 
// ****************************************************************************
//	Comments:
//		* The basic enumeration code is based on Maxim application note AN3690 
// ****************************************************************************

#include <avr/io.h>
#include <stdio.h>	// for debugging
#include "USB.h"
#include "USB_enum.h"
#include "usart.h"	// for debugging
#include "SPI.h"
#include "types.h"

extern volatile char TXbuf[50], TXbuf_len;	// for test messages

// USB variables
volatile u08 USB_configval;
volatile u08 USB_ep3stall;
volatile u08 USB_suspended;

void USB_setup_data(void) {
	u08 buf[8];

	USB_readFIFO(R4_SUDFIFO, buf, 8);	// read setup packet

	TXbuf_len = sprintf((char*)TXbuf, "type: %02X req: %02X Value: %02X %02X Index: %02X %02X Length: %02X %02X\r\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);	// create readable message*****
	dma_txbuf(&DMA.CH0, &USARTD0, DMA_CH_TRIGSRC_USARTD0_DRE_gc, (u08*)TXbuf, TXbuf_len);	// queue for transmission*****

	if ((buf[0]&0x60) == 0x00) {	// standard request
		switch (buf[1]) {
			case 0x00:	USB_setup_get_status(buf[0], buf[4]);		break;	// get status
			case 0x01:	USB_setup_clr_feature(buf[0], buf[2], buf[4]);	break;	// clear feature
			case 0x03:	USB_setup_set_feature(buf[0], buf[2], buf[4]);	break;	// set feature
			case 0x05:	USB_setup_set_address();	break;	// set address
			case 0x06:	USB_setup_get_descriptor(buf[2], buf[3], buf[6], buf[7]);	break;	// get descriptor
			case 0x08:	USB_setup_get_config();		break;	// get configuration
			case 0x09:	USB_setup_set_config(buf[2]);		break;	// set configuration
			case 0x0A:	USB_setup_get_interface(buf[4]);	break;	// get interface
			case 0x0B:	USB_setup_set_interface(buf[2], buf[4]);	break;	// set interface
			default:	SPI_RWreg(R9_EPSTALLS | WRITE, 0x23);	// stall CONTROL transfer
		}
	}
	else	// unknown request 
		SPI_RWreg(R9_EPSTALLS | WRITE, 0x23);	// stall CONTROL transfer
}

void USB_setup_get_status(u08 request_type, u08 index_L) {	// get status

	switch(request_type) {
		case 0x80:	// directed to device
			SPI_RWreg(R0_EP0FIFO | WRITE, 0x00);	// send remote wakeup (bit 1), self powered (bit 0)
			SPI_RWreg(R0_EP0FIFO | WRITE, 0x00);		// second byte always 0
			SPI_RWreg(R5_EP0BC | WRITE | ACKSTAT, 1);	// 2 bytes, ack
			break;
		case 0x81:	// directed to interface
			SPI_RWreg(R0_EP0FIFO | WRITE, 0x00);		// first byte always 0
			SPI_RWreg(R0_EP0FIFO | WRITE, 0x00);		// second byte always 0
			SPI_RWreg(R5_EP0BC | WRITE | ACKSTAT, 1);	// 2 bytes, ack
			break;
		case 0x82:	// directed to endpoint
			if (index_L == 0x83) {	// EP3
				SPI_RWreg(R0_EP0FIFO | WRITE, USB_ep3stall);		// stall bit (bit 0)
				SPI_RWreg(R0_EP0FIFO | WRITE, 0x00);		// second byte always 0
				SPI_RWreg(R5_EP0BC | WRITE | ACKSTAT, 1);	// 2 bytes, ack
			}
			else SPI_RWreg(R9_EPSTALLS | WRITE, 0x23);	// stall CONTROL transfer
			break;
		default:	SPI_RWreg(R9_EPSTALLS | WRITE, 0x23);	// stall CONTROL transfer
	}
}

void USB_setup_clr_feature(u08 request_type, u08 value_L, u08 index_L) {	// clear feature
	if ((request_type==0x02) && (value_L==0x00) && (index_L==0x83)) {	// recipient is endpoint, feature is EP halt, endpoint is EP3
		SPI_RWreg(R10_CLRTOGS | WRITE, 0x10);	// clear data toggle for EP3
		SPI_RWreg(R9_EPSTALLS | WRITE | ACKSTAT, SPI_RWreg(R9_EPSTALLS, 0)&(~0x10));	// clear STLEP3IN
		USB_ep3stall = 0;
	}
	else SPI_RWreg(R9_EPSTALLS | WRITE, 0x23);	// stall CONTROL transfer
}

void USB_setup_set_feature(u08 request_type, u08 value_L, u08 index_L) {	// set feature
	if ((request_type==0x02) && (value_L==0x00) && (index_L==0x83)) {	// recipient is endpoint, feature is EP halt, endpoint is EP3
		SPI_RWreg(R9_EPSTALLS | WRITE | ACKSTAT, SPI_RWreg(R9_EPSTALLS, 0)|0x10);	// set STLEP3IN
		USB_ep3stall = 1;
	}
	else SPI_RWreg(R9_EPSTALLS | WRITE, 0x23);	// stall CONTROL transfer
}

void USB_setup_set_address(void) {	// set address
	// address change is handled automatically by MAX3420, just ACK
	SPI_RWreg(R_DUMMY | ACKSTAT, 0);	// set ACKSTAT
}

void USB_setup_get_descriptor(u08 value_L, u08 value_H, u08 length_L, u08 length_H) {	// send descriptor to host
	u16 reqlen, sendlen, desclen;
	u08 *data;

	// MAX SIZE OF ANY DESCRIPTOR IS ONE PACKET (64 bytes)
	// will extend this function if needed...
	desclen = 0;
	reqlen = ((u16)length_H)<<8 | length_L;	// get length of request (16-bit)

	switch (value_H) {	// select appropriate descriptor
		case 0x01:	// device descriptor
			desclen = USB_DD[0];
			data = (u08*)USB_DD;
			break;
		case 0x02:	// configuration descriptor
			desclen = USB_CD[2];
			data = (u08*)USB_CD;
			break;
		case 0x03:	// string descriptor
			if (value_L < USB_NUM_STRINGS) {
				desclen = USB_SD[value_L][0];
				data = (u08*)USB_SD[value_L];
			}
			break;
		case 0x21:	// HID descriptor
			desclen = USB_CD[18];
			data = (u08*)&USB_CD[18];
			break;
		case 0x22:	// report descriptor
			desclen = USB_CD[25];
			data = (u08*)USB_RD;
			break;
	}
	if (desclen != 0) {
		sendlen = min_u16(reqlen, desclen);	// send smaller number of requested/available bytes
	//TXbuf_len = sprintf(TXbuf, "descr:%02X desclen %u sendlen %u data0%02X\r\n", buf[3], desclen, sendlen, data[0]);	// create readable message*****
	//dma_txbuf(&DMA.CH0, &USARTD0, DMA_CH_TRIGSRC_USARTD0_DRE_gc, TXbuf, TXbuf_len);	// queue for transmission*****
		USB_writeFIFO(R0_EP0FIFO, data, sendlen);	// write string to FIFO
		SPI_RWreg(R5_EP0BC | WRITE | ACKSTAT, sendlen);		// set no. bytes, ack
	}
	else SPI_RWreg(R9_EPSTALLS | WRITE, 0x23);	// stall CONTROL transfer
}

void USB_setup_get_config(void) {	// get configuration
	SPI_RWreg(R0_EP0FIFO | WRITE, USB_configval);	// send config value
	SPI_RWreg(R5_EP0BC | WRITE | ACKSTAT, 1);		// 1 byte, ack
}

void USB_setup_set_config(u08 value_L) {	// set configuration
	USB_configval = value_L;	// store new config value
	if (USB_configval != 0)	{	// if configured, enable suspend interrupt
		SPI_RWreg(R14_USBIEN | WRITE, SPI_RWreg(R14_USBIEN, 0)|0x10);	// enable SUSPIE interrupt
		SPI_RWreg(R_DUMMY | ACKSTAT, 0);	// set ACKSTAT
		SPI_RWreg(R20_IOPINS | WRITE, SPI_RWreg(R20_IOPINS, 0x0F)|0x04);	// LED on ******
	}
}

void USB_setup_get_interface(u08 index_L) {	// get interface
	if (index_L==0) {	// only for interface 0
		SPI_RWreg(R0_EP0FIFO | WRITE, 0);			// send interface value (0)
		SPI_RWreg(R5_EP0BC | WRITE | ACKSTAT, 1);	// 1 byte, ack
	}
	else SPI_RWreg(R9_EPSTALLS | WRITE, 0x23);	// stall CONTROL transfer
}

void USB_setup_set_interface(u08 value_L, u08 index_L) {	// set interface
	if ((value_L==0)&(index_L==0))	SPI_RWreg(R_DUMMY | ACKSTAT, 0);	// set ACKSTAT only for interface 0, alternate setting 0
	else SPI_RWreg(R9_EPSTALLS | WRITE, 0x23);	// stall CONTROL transfer
}

u16 min_u16(u16 a, u16 b) {
	if (a < b) return a;
	else return b;
}

